{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "f0da8dfb",
   "metadata": {},
   "source": [
    "# 函数\n",
    "\n",
    "## 定义函数\n",
    "\n",
    "函数，通常接受输入参数，并有返回值。它负责完成某项特定任务，而且相较于其他代码，具备相对的独立性。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "ebb0b46d",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, y):\n",
    "    \"\"\"Add two numbers\"\"\"\n",
    "    a = x + y\n",
    "    return a"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "44df4c48",
   "metadata": {},
   "source": [
    "函数通常有一下几个特征：\n",
    "\n",
    "- 使用 def 关键词来定义一个函数。\n",
    "- def 后面是函数的名称，括号中是函数的参数，不同的参数用 , 隔开， def foo(): 的形式是必须要有的，参数可以为空；\n",
    "- 使用缩进来划分函数的内容；\n",
    "- docstring 用 \"\"\" 包含的字符串，用来解释函数的用途，可省略；\n",
    "- return 返回特定的值，如果省略，返回 `None` 。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "939ac704",
   "metadata": {},
   "source": [
    "## 使用函数\n",
    "\n",
    "使用函数时，只需要将参数换成特定的值传给函数。\n",
    "\n",
    "Python并没有限定参数的类型，因此可以使用不同的参数类型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "26922d2f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(2, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "859ac8e3",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'foobar'"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add('foo', 'bar')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6e766665",
   "metadata": {},
   "source": [
    "传入的两个参数不可以相加，Python会将报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "eaa51f4b",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "unsupported operand type(s) for +: 'int' and 'str'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Input \u001b[0;32mIn [4]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43madd\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43mfoo\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "Input \u001b[0;32mIn [1]\u001b[0m, in \u001b[0;36madd\u001b[0;34m(x, y)\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21madd\u001b[39m(x, y):\n\u001b[1;32m      2\u001b[0m     \u001b[38;5;124;03m\"\"\"Add two numbers\"\"\"\u001b[39;00m\n\u001b[0;32m----> 3\u001b[0m     a \u001b[38;5;241m=\u001b[39m \u001b[43mx\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43my\u001b[49m\n\u001b[1;32m      4\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m a\n",
      "\u001b[0;31mTypeError\u001b[0m: unsupported operand type(s) for +: 'int' and 'str'"
     ]
    }
   ],
   "source": [
    "add(2, \"foo\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6028185c",
   "metadata": {},
   "source": [
    "传入的参数数目与实际不符合，也会报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "9f23fb82",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "add() takes 2 positional arguments but 3 were given",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Input \u001b[0;32mIn [5]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43madd\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m3\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: add() takes 2 positional arguments but 3 were given"
     ]
    }
   ],
   "source": [
    "add(1, 2, 3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "bad8e259",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "add() missing 1 required positional argument: 'y'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Input \u001b[0;32mIn [6]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43madd\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: add() missing 1 required positional argument: 'y'"
     ]
    }
   ],
   "source": [
    "add(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6895cd48",
   "metadata": {},
   "source": [
    "传入参数时，Python提供了两种选项，第一种是上面使用的按照位置传入参数，另一种则是使用关键词模式，显式地指定参数的值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "bdd03fff",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(x=2, y=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "fab15862",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'barfoo'"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(y=\"foo\", x=\"bar\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1699a007",
   "metadata": {},
   "source": [
    "可以混合这两种模式："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "cee28761",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "5"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(2, y=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a462723f",
   "metadata": {},
   "source": [
    "## 设定参数默认值\n",
    "\n",
    "可以在函数定义的时候给参数设定默认值，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2eaeb347",
   "metadata": {},
   "outputs": [],
   "source": [
    "def quad(x, a=1, b=0, c=0):\n",
    "    return a*x**2 + b*x + c"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "9e85e270",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4.0"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "quad(2.0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "727e70e4",
   "metadata": {},
   "source": [
    "可以修改参数的默认值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "f2ec3236",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10.0"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "quad(2.0, b=3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "31cd04be",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "12.0"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "quad(2.0, 2, c=4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b9b3bc4d",
   "metadata": {},
   "source": [
    "这里混合了位置和指定两种参数传入方式，第二个2是传给 a 的。\n",
    "\n",
    "注意，在使用混合语法时，要注意不能给同一个值赋值多次，否则会报错，例如："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "fc960c32",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "quad() got multiple values for argument 'a'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "Input \u001b[0;32mIn [14]\u001b[0m, in \u001b[0;36m<cell line: 1>\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[43mquad\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m2.0\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43ma\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m2\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "\u001b[0;31mTypeError\u001b[0m: quad() got multiple values for argument 'a'"
     ]
    }
   ],
   "source": [
    "quad(2.0, 2, a=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b36a7037",
   "metadata": {},
   "source": [
    "## 接收不定参数\n",
    "\n",
    "使用如下方法，可以使函数接受不定数目的参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "555c5b63",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, *args):\n",
    "    total = x\n",
    "    for arg in args:\n",
    "        total += arg\n",
    "    return total"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42bb8b39",
   "metadata": {},
   "source": [
    "`*args` 表示参数数目不定，可以看成一个元组，把第一个参数后面的参数当作元组中的元素："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "92d51afc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "10"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(1, 2, 3, 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "e9098467",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(1, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "757fecaf",
   "metadata": {},
   "source": [
    "这样定义的函数不能使用关键词传入参数，要使用关键词，可以这样："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "7b28ef56",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(x, **kwargs):\n",
    "    total = x\n",
    "    for arg, value in kwargs.items():\n",
    "        print(\"adding \", arg)\n",
    "        total += value\n",
    "    return total"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e008d80f",
   "metadata": {},
   "source": [
    "`**kwargs` 表示参数数目不定，相当于一个字典，关键词和值对应于键值对："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "c4efdc7a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "adding  y\n",
      "adding  z\n",
      "adding  w\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "46"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(10, y=11, z=12, w=13)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "95fff570",
   "metadata": {},
   "source": [
    "可以接收任意数目的位置参数和键值对参数的函数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "7208b698",
   "metadata": {},
   "outputs": [],
   "source": [
    "def foo(*args, **kwargs):\n",
    "    print(f'{args=}, {kwargs=}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "224f896e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "args=(2, 3), kwargs={'x': 'bar', 'z': 10}\n"
     ]
    }
   ],
   "source": [
    "foo(2, 3, x='bar', z=10)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f9c5f404",
   "metadata": {},
   "source": [
    "## 返回多个值\n",
    "\n",
    "函数可以返回多个值："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "55ea32eb",
   "metadata": {},
   "outputs": [],
   "source": [
    "from math import atan2\n",
    "\n",
    "def to_polar(x, y):\n",
    "    r = (x**2 + y**2) ** 0.5\n",
    "    theta = atan2(y, x)\n",
    "    return r, theta\n",
    "\n",
    "r, theta = to_polar(3, 4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "72afd0bf",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(5.0, 0.9272952180016122)"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "r, theta"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c349c17a",
   "metadata": {},
   "source": [
    "Python将返回的两个值变成了元组："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "28c53f51",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(5.0, 0.9272952180016122)"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "to_polar(3, 4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "10baf672",
   "metadata": {},
   "source": [
    "## 函数类型注释\n",
    "\n",
    "Python 3支持对函数的类型进行注释，但是不会真正检验对象的类型："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "ed054705",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(a : int, b : int) -> int: \n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "1b926f71",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(1, 2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f160b693",
   "metadata": {},
   "source": [
    "类型不是int也不会报错："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "374cdc48",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'ab'"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(\"a\", \"b\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "bc98cd50",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
